
#ifndef _TTG_STRUCTURES_LOOPBACKS_
#define _TTG_STRUCTURES_LOOPBACKS_
#include "Common/Definitions.h"

#include <list>

#include "Grouping.h"
#include "Objects.h"

namespace ttg
{
	namespace structures
	{
		//Essence, that provides object+method concatenation.
		template <class Ret, class Arg>
		class Delegate
		{
			public:
				//Will be called by handler.
				virtual Ret notify(Arg message) = 0;
		};
	}
}

namespace ttg_internal
{
	namespace structures
	{
		//Invokable Object, that wraps delegate.
		template <class Arg>
		class InvokableObject_Delegate :public ttg::structures::InvokableObject
		{
			private:
				Arg arg;
				ttg::structures::Delegate<void, Arg> *d;
			public:
				//Note: Delegate will be released by destructor!
				inline InvokableObject_Delegate(ttg::structures::Delegate<void, Arg> *d, Arg arg)
				{
					this->arg = arg;
					this->d = d;
				}
				//InvokableObject member. Notifies delegated object.
				virtual void invoke()
				{ d->notify(arg); }
				~InvokableObject_Delegate()
				{
					SAFE_DELETE(d);
				}
		};
		//Delegate, that contains function.
		template <class Ret, class Arg>
		class Delegate_Func :public ttg::structures::Delegate<Ret, Arg>
		{
			private:
				Ret (*func)(Arg);
			public:
				inline Delegate_Func(Ret (*func)(Arg))
				{
					this->func = func;
				}
				virtual Ret notify(Arg message)
				{ return func(message); }
		};
		//Delegate, that contains static method and receives reference to owner.
		template <class Object, class Method, class Ret, class Arg>
		class Delegate_Static :public ttg::structures::Delegate<Ret, Arg>
		{
			private:
				Object *object;
				Method method;
			public:
				inline Delegate_Static(Object *object, Method method)
				{
					this->object = object;
					this->method = method;
				}
				virtual Ret notify(Arg message)
				{ return method(object, message); }
		};
		//Delegate, that contains regular method and receives reference to owner.
		template <class Object, class Method, class Ret, class Arg>
		class Delegate_Dynamic :public ttg::structures::Delegate<Ret, Arg>
		{
			private:
				Object *object;
				Method method;
			public:
				inline Delegate_Dynamic(Object *object, Method method)
				{
					this->object = object;
					this->method = method;
				}
				virtual Ret notify(Arg message)
				{ return (object->*method)(message); }
		};
		//Represent loopback, that can return result.
		//.NET analogue is event.
		template <class Ret, class Arg>
		class RetLoopback
		{
			protected:
				std::list<ttg::structures::Delegate<Ret, Arg> *> registered;
			public:
				//Adds delegate to loopback (at front).
				//Use make_delegate() wrapper for delegate creation.
				//Note: "d" will be deleted by destructor.
				inline void addFirst(ttg::structures::Delegate<Ret, Arg> *d)
				{ registered.push_front(d); }
				//Adds delegate to loopback (at back).
				//Use make_delegate() wrapper for delegate creation.
				//Note: "d" will be deleted by destructor.
				inline void addLast(ttg::structures::Delegate<Ret, Arg> *d)
				{ registered.push_back(d); }
				//Clears all registered delegates.
				inline void clear()
				{ registered.clear(); }
				//Adds delegate to loopback.
				//Use make_delegate() wrapper for delegate creation.
				//Note: "d" will be deleted by destructor.
				inline void operator +=(ttg::structures::Delegate<Ret, Arg> *d)
				{ addLast(d); }
				//Releases all references.
				virtual ~RetLoopback()
				{ SAFE_DELETE_CONTENT(&registered); }
		};
	}
}

namespace ttg
{
	namespace structures
	{
		//Represent loopback set.
		//.NET analogue is event.
		template <class T>
		class Loopback :public ttg_internal::structures::RetLoopback<void, T>
		{
			public:
				inline void notify(T message)
				{
					std::list<Delegate<void, T> *>::iterator it = registered.begin();
					while (it != registered.end())
					{
						(*it)->notify(message);
						it++;
					}
				}
		};
		//Represent loopback, that can return result.
		//.NET analogue is event.
		template <class Ret, class Arg>
		class RetLoopback :public ttg_internal::structures::RetLoopback<Ret, Arg>
		{
			public:
				//Notifies all registered delegates about event.
				//Returns result from the last delegate.
				inline Ret notify(Arg message)
				{
					std::list<Delegate<Ret, Arg> *>::iterator it = registered.begin();
					Ret res;
					while (it != registered.end())
					{
						res = (*it)->notify(message);
						it++;
					}
					return res;
				}
		};
		//Wraps static method as delegate.
		template <class Ret, class Arg>
		Delegate<Ret, Arg> *make_delegate(Ret (*func)(Arg))
		{
			return new ttg_internal::structures::Delegate_Func<Ret, Arg>(func);
		}
		//Wraps static method + object reference as delegate.
		template <class Object, class Ret, class Arg>
		Delegate<Ret, Arg> *make_delegate(Object *object, Ret (*method)(Object *, Arg))
		{
			return new ttg_internal::structures::Delegate_Static<Object,
																 Ret (*)(Object *, Arg),
																 Ret, Arg>(object, method);
		}
		//Wraps regular method + object reference as delegate.
		template <class Object, class Ret, class Arg>
		Delegate<Ret, Arg> *make_delegate(Object *object, Ret (Object::*method)(Arg))
		{
			return new ttg_internal::structures::Delegate_Dynamic<Object,
																  Ret (Object::*)(Arg),
																  Ret, Arg>(object, method);
		}
		//Wraps delegate into invokable object.
		//Note: "d" will be released by InvokableObject destructor.
		template <class Arg>
		InvokableObject *make_invokable_object(Delegate<void, Arg> *d, Arg value)
		{ return new ttg_internal::structures::InvokableObject_Delegate<Arg>(d, value); }
	}
}

#endif
